// --------------------------------------------------------------------------------------------------------------------
// <copyright file="OrderPlugin.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge
{
    #region Usings

    using System;
    using System.Collections.Generic;

    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Events;
    using Org.OpenTrader.Framework.Forge.Interfaces;
    using Org.OpenTrader.Framework.LiveObjects.Interfaces;

    #endregion

    /// <summary>
    /// The order plugin.
    /// </summary>
    public abstract class OrderPlugin : LayerPlugin, IOrderPlugin
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="OrderPlugin"/> class.
        /// </summary>
        /// <param name="descriptor">
        /// The descriptor.
        /// </param>
        /// <param name="container">
        /// The container.
        /// </param>
        protected OrderPlugin(ILiveObjectDescriptor descriptor, ILiveObjectContainer container)
            : base(descriptor, container)
        {
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets OrderRoutingConfigurationSchema.
        /// </summary>
        public string OrderRoutingConfigurationSchema
        {
            get
            {
                return "OrderRoutingConfigurationSchema";
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The fire event_ ack cancel.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="cancelledQuantity">
        /// The cancelledQuantity.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventAckCancel(Guid orderId, EOrderWay way, int cancelledQuantity, RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Status(orderId, way, EOrderStatus.AckCancel, cancelledQuantity));
        }

        /// <summary>
        /// The fire event_ ack modify.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="requestedQuantity">
        /// The requestedQuantity.
        /// </param>
        /// <param name="remainingQuantity">
        /// The remainingQuantity.
        /// </param>
        /// <param name="requestedPrice">
        /// The requestedPrice.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventAckModify(
            Guid orderId, EOrderWay way, int requestedQuantity, int remainingQuantity, double requestedPrice, RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Status(orderId, way, EOrderStatus.AckModify, requestedQuantity, remainingQuantity, requestedPrice));
        }

        /// <summary>
        /// The fire event_ ack order.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="requestedQuantity">
        /// The requestedQuantity.
        /// </param>
        /// <param name="remainingQuantity">
        /// The remainingQuantity.
        /// </param>
        /// <param name="requestedPrice">
        /// The requestedPrice.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventAckOrder(
            Guid orderId, EOrderWay way, int requestedQuantity, int remainingQuantity, double requestedPrice, RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Status(orderId, way, EOrderStatus.AckOrder, requestedQuantity, remainingQuantity, requestedPrice));
        }

        /// <summary>
        /// The fire event_ order execution.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="executedPrice">
        /// The executedPrice.
        /// </param>
        /// <param name="executedQuantity">
        /// The executedQuantity.
        /// </param>
        /// <param name="remainingQuantity">
        /// The remainingQuantity.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventOrderExecution(
            Guid orderId, EOrderWay way, double executedPrice, int executedQuantity, int remainingQuantity, RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Execution(orderId, Timings.Now, way, executedPrice, executedQuantity, remainingQuantity));
        }

        /// <summary>
        /// The fire event_ order execution.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="executedPrice">
        /// The executedPrice.
        /// </param>
        /// <param name="executedQuantity">
        /// The executedQuantity.
        /// </param>
        /// <param name="remainingQuantity">
        /// The remainingQuantity.
        /// </param>
        /// <param name="counterparty">
        /// The counterparty.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventOrderExecution(
            Guid orderId, 
            EOrderWay way, 
            double executedPrice, 
            int executedQuantity, 
            int remainingQuantity, 
            string counterparty, 
            RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Execution(orderId, Timings.Now, way, executedPrice, executedQuantity, remainingQuantity, counterparty));
        }

        /// <summary>
        /// The fire event_ out report.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventOutReport(Guid orderId, EOrderWay way, RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Status(orderId, way, EOrderStatus.OutReport));
        }

        /// <summary>
        /// The fire event_ rej cancel.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="rejectReason">
        /// The rejectReason.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventRejCancel(Guid orderId, EOrderWay way, string rejectReason, RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Status(orderId, way, EOrderStatus.RejCancel, rejectReason));
        }

        /// <summary>
        /// The fire event_ rej modify.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="rejectReason">
        /// The rejectReason.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventRejModify(Guid orderId, EOrderWay way, string rejectReason, RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Status(orderId, way, EOrderStatus.RejModify, rejectReason));
        }

        /// <summary>
        /// The fire event_ rej order.
        /// </summary>
        /// <param name="orderId">
        /// The orderId.
        /// </param>
        /// <param name="way">
        /// The way.
        /// </param>
        /// <param name="rejectReason">
        /// The rejectReason.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public void RaiseEventRejOrder(Guid orderId, EOrderWay way, string rejectReason, RemotableEventHandler<OrderEventArgs> handler)
        {
            handler.RaiseEvent(this, new OrderEventArgs.Status(orderId, way, EOrderStatus.RejOrder, rejectReason));
        }

        #endregion

        #region Implemented Interfaces

        #region IOrderPlugin

        /// <summary>
        /// The cancel.
        /// </summary>
        /// <param name="wiring">
        /// The wiring.
        /// </param>
        /// <param name="order">
        /// The order.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public abstract Status Cancel(IOrderWiring wiring, IOrder order, RemotableEventHandler<OrderEventArgs> handler);

        /// <summary>
        /// The modify.
        /// </summary>
        /// <param name="wiring">
        /// The wiring.
        /// </param>
        /// <param name="oldOrder">
        /// The oldOrder.
        /// </param>
        /// <param name="newOrder">
        /// The newOrder.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public abstract Status Modify(IOrderWiring wiring, IOrder oldOrder, IOrder newOrder, RemotableEventHandler<OrderEventArgs> handler);

        /// <summary>
        /// The send.
        /// </summary>
        /// <param name="wiring">
        /// The wiring.
        /// </param>
        /// <param name="order">
        /// The order.
        /// </param>
        /// <param name="handler">
        /// The handler.
        /// </param>
        public abstract Status Send(IOrderWiring wiring, IOrder order, RemotableEventHandler<OrderEventArgs> handler);

        #endregion

        #endregion

        /// <summary>
        /// This class is a store for handlers. Each handler is stored for a particular order id.
        /// </summary>
        public class HandlersStore
        {
            #region Constants and Fields

            /// <summary>
            /// The handlers, each one being kept for a particular subscription key.
            /// </summary>
            private readonly IDictionary<Guid, RemotableEventHandler<OrderEventArgs>> handlers = new Dictionary<Guid, RemotableEventHandler<OrderEventArgs>>();

            #endregion

            #region Public Methods

            /// <summary>
            /// The clear handlers.
            /// </summary>
            public void Clear()
            {
                this.handlers.Clear();
            }

            /// <summary>
            /// The get handler.
            /// </summary>
            /// <param name="orderID">
            /// The subscription Key.
            /// </param>
            /// <returns>
            /// </returns>
            public RemotableEventHandler<OrderEventArgs> GetHandler(Guid orderID)
            {
                if (this.handlers.ContainsKey(orderID))
                {
                    return this.handlers[orderID];
                }

                return null;
            }

            /// <summary>
            /// The has handler.
            /// </summary>
            /// <param name="orderID">
            /// The feed wiring.
            /// </param>
            /// <returns>
            /// The has handler.
            /// </returns>
            public bool HasHandler(Guid orderID)
            {
                return this.handlers.ContainsKey(orderID);
            }

            /// <summary>
            /// The set handler.
            /// </summary>
            /// <param name="orderID">
            /// The subscription Key.
            /// </param>
            /// <param name="handler">
            /// The handler.
            /// </param>
            public void SetHandler(Guid orderID, RemotableEventHandler<OrderEventArgs> handler)
            {
                this.handlers[orderID] = handler;
            }

            /// <summary>
            /// The unset handler.
            /// </summary>
            /// <param name="orderID">
            /// The feed wiring.
            /// </param>
            public void UnsetHandler(Guid orderID)
            {
                if (this.handlers.ContainsKey(orderID))
                {
                    this.handlers.Remove(orderID);
                }
            }

            #endregion
        }

        /// <summary>
        /// Keep forward and backward mappings for each order request.
        /// Each order is held using it's internal identifier (Guid) and it's external identifier (ExternalOrderIdentifierType)
        /// </summary>
        /// <typeparam name="ExternalOrderIdentifierType">
        /// The type used on external side to identify an order. InternalIdentifierType is Guid
        /// </typeparam>
        public class OrdersStore<ExternalOrderIdentifierType>
        {
            #region Constants and Fields

            /// <summary>
            /// ExternalOrderIdentifierType -> order
            /// </summary>
            private readonly IDictionary<ExternalOrderIdentifierType, IOrder> ordersPerExternal = new Dictionary<ExternalOrderIdentifierType, IOrder>();

            /// <summary>
            /// Guid -> order
            /// </summary>
            private readonly IDictionary<Guid, IOrder> ordersPerInternal = new Dictionary<Guid, IOrder>();

            #endregion

            #region Public Methods

            /// <summary>
            /// The clear.
            /// </summary>
            public void Clear()
            {
                this.ordersPerInternal.Clear();
                this.ordersPerExternal.Clear();
            }

            /// <summary>
            /// The get order.
            /// </summary>
            /// <param name="externalOrderIdentifier">
            /// The external order identifier.
            /// </param>
            /// <returns>
            /// </returns>
            public IOrder GetOrder(ExternalOrderIdentifierType externalOrderIdentifier)
            {
                if (this.ordersPerExternal.ContainsKey(externalOrderIdentifier))
                {
                    return this.ordersPerExternal[externalOrderIdentifier];
                }

                return null;
            }

            /// <summary>
            /// The get order.
            /// </summary>
            /// <param name="internalOrderIdentifier">
            /// The internal order identifier.
            /// </param>
            /// <returns>
            /// </returns>
            public IOrder GetOrder(Guid internalOrderIdentifier)
            {
                if (this.ordersPerInternal.ContainsKey(internalOrderIdentifier))
                {
                    return this.ordersPerInternal[internalOrderIdentifier];
                }

                return null;
            }

            /// <summary>
            /// The has order.
            /// </summary>
            /// <param name="internalOrderIdentifier">
            /// The internal order identifier.
            /// </param>
            /// <returns>
            /// The has order.
            /// </returns>
            public bool HasOrder(Guid internalOrderIdentifier)
            {
                return this.ordersPerInternal.ContainsKey(internalOrderIdentifier);
            }

            /// <summary>
            /// The has order.
            /// </summary>
            /// <param name="externalOrderIdentifier">
            /// The external order identifier.
            /// </param>
            /// <returns>
            /// The has order.
            /// </returns>
            public bool HasOrder(ExternalOrderIdentifierType externalOrderIdentifier)
            {
                return this.ordersPerExternal.ContainsKey(externalOrderIdentifier);
            }

            /// <summary>
            /// The set order.
            /// </summary>
            /// <param name="order">
            /// The order.
            /// </param>
            /// <param name="externalOrderIdentifier">
            /// The external order identifier.
            /// </param>
            public void SetOrder(IOrder order, ExternalOrderIdentifierType externalOrderIdentifier)
            {
                this.ordersPerInternal[order.ID] = order;
            }

            /// <summary>
            /// The unset order.
            /// </summary>
            /// <param name="internalOrderIdentifier">
            /// The internal order identifier.
            /// </param>
            /// <param name="externalOrderIdentifier">
            /// The external order identifier.
            /// </param>
            public void UnsetOrder(Guid internalOrderIdentifier, ExternalOrderIdentifierType externalOrderIdentifier)
            {
                if (this.HasOrder(internalOrderIdentifier))
                {
                    this.ordersPerInternal.Remove(internalOrderIdentifier);
                    this.ordersPerExternal.Remove(externalOrderIdentifier);
                }
            }

            #endregion
        }
    }
}